/*
 * Copyright 2011 OpenWAF.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package com.openwaf.client.dom;

import com.openwaf.client.core.Client;
import com.openwaf.client.event.Event;
import com.openwaf.client.event.EventHandler;
import com.openwaf.client.event.EventManager;
import com.openwaf.client.event.HandlerRegistration;
import com.openwaf.client.event.dom.BlurEvent;
import com.openwaf.client.event.dom.BlurHandler;
import com.openwaf.client.event.dom.ChangeEvent;
import com.openwaf.client.event.dom.ChangeHandler;
import com.openwaf.client.event.dom.ClickEvent;
import com.openwaf.client.event.dom.ClickHandler;
import com.openwaf.client.event.dom.ContextMenuEvent;
import com.openwaf.client.event.dom.ContextMenuHandler;
import com.openwaf.client.event.dom.DoubleClickEvent;
import com.openwaf.client.event.dom.DoubleClickHandler;
import com.openwaf.client.event.dom.FocusEvent;
import com.openwaf.client.event.dom.FocusHandler;
import com.openwaf.client.event.dom.HasAllKeyHandlers;
import com.openwaf.client.event.dom.HasAllMouseHandlers;
import com.openwaf.client.event.dom.HasBlurHandlers;
import com.openwaf.client.event.dom.HasChangeHandlers;
import com.openwaf.client.event.dom.HasClickHandlers;
import com.openwaf.client.event.dom.HasContextMenuHandlers;
import com.openwaf.client.event.dom.HasDoubleClickHandlers;
import com.openwaf.client.event.dom.HasFocusHandlers;
import com.openwaf.client.event.dom.KeyDownEvent;
import com.openwaf.client.event.dom.KeyDownHandler;
import com.openwaf.client.event.dom.KeyPressEvent;
import com.openwaf.client.event.dom.KeyPressHandler;
import com.openwaf.client.event.dom.KeyUpEvent;
import com.openwaf.client.event.dom.KeyUpHandler;
import com.openwaf.client.event.dom.LoadEvent;
import com.openwaf.client.event.dom.LoadHandler;
import com.openwaf.client.event.dom.MouseDownEvent;
import com.openwaf.client.event.dom.MouseDownHandler;
import com.openwaf.client.event.dom.MouseMoveEvent;
import com.openwaf.client.event.dom.MouseMoveHandler;
import com.openwaf.client.event.dom.MouseOutEvent;
import com.openwaf.client.event.dom.MouseOutHandler;
import com.openwaf.client.event.dom.MouseOverEvent;
import com.openwaf.client.event.dom.MouseOverHandler;
import com.openwaf.client.event.dom.MouseUpEvent;
import com.openwaf.client.event.dom.MouseUpHandler;
import com.openwaf.common.annotation.NoJavaScript;


public class Element extends Node implements
        HasAllKeyHandlers,
        HasAllMouseHandlers,
        HasBlurHandlers,
        HasChangeHandlers,
        HasClickHandlers,
        HasContextMenuHandlers,
        HasDoubleClickHandlers,
        HasFocusHandlers {

    /**
     *
     */
    protected Element() {
    }

    public final native Style getStyle() /*-{
    return this.style;
    }-*/;

    /**
     *
     */
    @NoJavaScript
    public final native void blur();

    /**
     *
     */
    @NoJavaScript
    public final native void focus();

    /**
     *
     * @return
     */
    public final native String getCssClassName() /*- {
    return this.className;
    } -*/;

    /**
     *
     * @return
     */
    public final native int getClientHeight() /*- {
    return this.clientHeight;
    } -*/;

    /**
     *
     * @return
     */
    public final native int getClientWidth() /*- {
    return this.clientWidth;
    } -*/;

    /**
     *
     * @return
     */
    public final native String getDir() /*- {
    return this.dir;
    } -*/;

    @NoJavaScript
    public final native Node[] getElementsByTagName(String tagname);

    /**
     *
     * @return
     */
    public final native String getId() /*- {
    return this.id;
    } -*/;

    /**
     *
     * @return
     */
    public final native String getInnerHTML() /*- {
    return this.innerHTML;
    } -*/;

    /**
     *
     * @return
     */
    public final native String getInnerText() /*- {
    return this.textContent;
    } -*/;

    /**
     *
     * @return
     */
    public final native String getTagName() /*- {
    return this.tagName;
    } -*/;

    /**
     *
     * @return
     */
    public final native String getLang() /*- {
    return this.lang;
    } -*/;

    /**
     *
     * @return
     */
    public final native int getOffsetHeight() /*- {
    return this.offsetHeight || 0;
    } -*/;

    /**
     *
     * @return
     */
    public final native int getOffsetLeft() /*- {
    return this.offsetLeft || 0;
    } -*/;

    /**
     *
     * @return
     */
    public final native int getOffsetTop() /*- {
    return this.offsetTop || 0;
    } -*/;

    /**
     *
     * @return
     */
    public final native int getOffsetWidth() /*- {
    return this.offsetWidth || 0;
    } -*/;

    /**
     *
     * @return
     */
    public final native Element getOffsetParent() /*- {
    return this.offsetParent;
    } -*/;

    /**
     *
     * @return
     */
    public final native int getScrollTop() /*- {
    return this.scrollTop || 0;
    } -*/;

    /**
     *
     * @return
     */
    public final native int getScrollLeft() /*- {
    return this.scrollLeft || 0;
    } -*/;

    /**
     *
     * @return
     */
    public final native String getTitle() /*- {
    return this.title;
    } -*/;

    /**
     *
     * @param title
     */
    public final native void setTitle(String title) /*- {
    this.title=title || '';
    } -*/;

    /**
     *
     * @param attrName
     * @param attrValue
     */
    @NoJavaScript
    public final native void setAttribute(String attrName, String attrValue);

    /**
     *
     * @param name
     * @return
     */
    @NoJavaScript
    public final native int removeAttribute(String name);

    /**
     *
     * @param className
     */
    public final native void setCssClassName(String className) /*- {
    this.className=className;
    } -*/;
    public final void addCssClassName(String name){
        throw new UnsupportedOperationException("addCssClassName");
    }
    public final void removeCssClassName(String name){
        throw new UnsupportedOperationException();
    }
    public final String[] getCssClassNames(){
        throw new UnsupportedOperationException();
    }
    

    /**
     *
     * @param dir
     */
    public final native void setDir(String dir) /*- {
    this.dir=dir;
    } -*/;

    /**
     *
     * @param id
     */
    public final native void setId(String id) /*- {
    this.id=id;
    } -*/;

    /**
     *
     * @param html
     */
    public final native void setInnerHTML(String html) /*- {
    this.innerHTML = html || '';
    } -*/;

    /**
     *
     * @param lang
     */
    public final native void setLang(String lang) /*- {
    this.lang=lang;
    } -*/;

    /**
     *
     * @param name
     * @param value
     */
    public final native void setPropertyBoolean(String name, boolean value) /*- {
    try{this[name]=value;}catch(e){}
    } -*/;

    /**
     *
     * @param name
     * @return
     */
    public final native boolean getPropertyBoolean(String name) /*- {
    return !!this[name];
    } -*/;

    /**
     *
     * @param name
     * @param value
     */
    public final native void setPropertyDouble(String name, double value) /*- {
    try{this[name]=value;}catch(e){}
    } -*/;

    /**
     *
     * @param name
     * @return
     */
    public final native double getPropertyDouble(String name) /*- {
    return parseFloat(this[name]) || 0.0;
    } -*/;

    /**
     *
     * @param name
     * @param value
     */
    public final native void setPropertyInt(String name, int value) /*- {
    try{this[name]=value;}catch(e){}
    } -*/;

    /**
     *
     * @param name
     * @return
     */
    public final native int getPropertyInt(String name) /*- {
    return parseInt(this[name]) || 0;
    } -*/;

    /**
     *
     * @param name
     * @param value
     */
    public final native void setPropertyObject(String name, Object value) /*- {
    try{this[name]=value;}catch(e){}
    } -*/;

    /**
     *
     * @param name
     * @return
     */
    public final native Object getPropertyObject(String name) /*- {
    return this[name] || null;
    } -*/;

    /**
     *
     * @param name
     * @param value
     */
    public final native void setPropertyString(String name, String value) /*- {
    try{this[name]=value;}catch(e){}
    } -*/;

    /**
     *
     * @param name
     * @return
     */
    public final native String getPropertyString(String name) /*- {
    return this[name] || null;
    } -*/;

    /**
     *
     * @param scrollTop
     */
    public final native void setScrollTop(int scrollTop) /*- {
    this.scrollTop=scrollTop;
    } -*/;

    /**
     *
     * @param tabIndex
     */
    public final native void setTabIndex(int tabIndex) /*- {
    this.tabIndex=tabIndex;
    } -*/;
    public final native int getTabIndex()/*-{
        return this.tabIndex;
    }-*/;
    private EventManager __eventmanager;

    private void handleNativeEvent(NativeEvent nativeEvent){
       Event e=createEvent(nativeEvent);
       __eventmanager.fireEvent(nativeEvent.getType(),e);
    }
    private  void  registerEventListener(String name){
        if(false){
            //just to mark it as used
            //TODO:remove
            this.handleNativeEvent(null);
        }
        if(Client.IE && (!Client.IE_9_OR_ABOVE)){
             __registerEventIE(this, name);
        }else{
            __registerEvent(this, name);
        }
    }
    private  void  unregisterEventListener(String name){
        if(Client.IE && (!Client.IE_9_OR_ABOVE)){
            __unregisterEventIE(this, name);
        }else{
            __unregisterEvent(this, name);
        }
    }
    private native void __registerEvent(Element src,String name)/*-{
        src.addEventListener(name,this.__eh,false);
    }-*/;
    private native void __unregisterEvent(Element src,String name)/*-{
        src.removeEventListener(name,this.__eh,false);
    }-*/;
     private native void __registerEventIE(Element src,String name)/*-{
        src.attachEvent("on"+name,this.__eh);
    }-*/;
    private native void __unregisterEventIE(Element src,String name)/*-{
        src.detachEvent("on"+name,this.__eh);
    }-*/;
    //TODO:i have used a trick here in order to support IE6 , if you are planngin to access DOM classes in native mode
    // please dont do that
    // will update on this soon
    private native void assignEventManagerRef(EventManager ref)/*-{
           this.__em=ref;
           var s=this;
           this.__eh=function(e){ s.@com.openwaf.client.dom.Element::handleNativeEvent(Lcom/openwaf/client/dom/NativeEvent;)(e);};

    }-*/;
    protected HandlerRegistration addEventHandler(final EventHandler handler) {
        if (this.__eventmanager == null) {
            this.__eventmanager = new EventManager();
            this.assignEventManagerRef(__eventmanager);
        }
        final String type=getEventHandlerType(handler);
        registerEventListener(type);
        this.__eventmanager.addEventHandler(type, handler);
        return new HandlerRegistration() {
            public void removeHandler() {
                __eventmanager.removeEventHandler(type, handler);
                if(!__eventmanager.hasEventHandlersForType(type)){
                   unregisterEventListener(type);
                }
            }
        };
    }

    public void removeEventHandler(EventHandler handler){
         if (__eventmanager != null) {
              String type=getEventHandlerType(handler);
              __eventmanager.removeEventHandler(type, handler);
               if(!__eventmanager.hasEventHandlersForType(type)){
                   unregisterEventListener(type);
              }
         }
    }
    public boolean hasEventHandlersForType(String type){
        return __eventmanager==null ? false: __eventmanager.hasEventHandlersForType(type);
    }
    public void fireEvent(Event<?> event) {
        if (this.__eventmanager != null) {
            this.__eventmanager.fireEvent(getEventType(event),event);
        }
    }

    public HandlerRegistration addKeyDownHandler(KeyDownHandler handler) {
        return this.addEventHandler(handler);
    }

    public HandlerRegistration addKeyUpHandler(KeyUpHandler handler) {
        return this.addEventHandler(handler);
    }

    public HandlerRegistration addKeyPressHandler(KeyPressHandler handler) {
        return this.addEventHandler(handler);
    }

    public HandlerRegistration addMouseDownHandler(MouseDownHandler handler) {
        return this.addEventHandler(handler);
    }

    public HandlerRegistration addMouseMoveHandler(MouseMoveHandler handler) {
        return this.addEventHandler(handler);
    }

    public HandlerRegistration addMouseOutHandler(MouseOutHandler handler) {
        return this.addEventHandler(handler);
    }

    public HandlerRegistration addMouseOverHandler(MouseOverHandler handler) {
        return this.addEventHandler(handler);
    }

    public HandlerRegistration addMouseUpHandler(MouseUpHandler handler) {
        return this.addEventHandler(handler);
    }

    public HandlerRegistration addBlurHandler(BlurHandler handler) {
        return this.addEventHandler(handler);
    }

    public HandlerRegistration addChangeHandler(ChangeHandler handler) {
        return this.addEventHandler(handler);
    }

    public HandlerRegistration addClickHandler(ClickHandler handler) {
        return this.addEventHandler(handler);
    }

    public HandlerRegistration addContextMenuHandler(ContextMenuHandler handler) {
        return this.addEventHandler(handler);
    }

    public HandlerRegistration addDoubleClickHandler(DoubleClickHandler handler) {
        return this.addEventHandler(handler);
    }

    public HandlerRegistration addFocusHandler(FocusHandler handler) {
        return this.addEventHandler(handler);
    }
    private static Event createEvent(NativeEvent nativeEvent){
        String name=nativeEvent.getType();
        if(name.equalsIgnoreCase(BlurEvent.NAME)){
            return new BlurEvent(nativeEvent);
        }
        if(name.equalsIgnoreCase(ChangeEvent.NAME)){
            return new ChangeEvent(nativeEvent);
        }
        if(name.equalsIgnoreCase(ClickEvent.NAME)){
            return new ClickEvent(nativeEvent);
        }
        if(name.equalsIgnoreCase(ContextMenuEvent.NAME)){
            return new ContextMenuEvent(nativeEvent);
        }
        if(name.equalsIgnoreCase(DoubleClickEvent.NAME)){
            return new DoubleClickEvent(nativeEvent);
        }
        if(name.equalsIgnoreCase(FocusEvent.NAME)){
            return new FocusEvent(nativeEvent);
        }
        if(name.equalsIgnoreCase(KeyDownEvent.NAME)){
            return new KeyDownEvent(nativeEvent);
        }
        if(name.equalsIgnoreCase(KeyPressEvent.NAME)){
            return new KeyPressEvent(nativeEvent);
        }
        if(name.equalsIgnoreCase(KeyUpEvent.NAME)){
            return new KeyUpEvent(nativeEvent);
        }
        if(name.equalsIgnoreCase(LoadEvent.NAME)){
            return new LoadEvent(nativeEvent);
        }
        if(name.equalsIgnoreCase(MouseDownEvent.NAME)){
            return new MouseDownEvent(nativeEvent);
        }
        if(name.equalsIgnoreCase(MouseMoveEvent.NAME)){
            return new MouseMoveEvent(nativeEvent);
        }
        if(name.equalsIgnoreCase(MouseOutEvent.NAME)){
            return new MouseOutEvent(nativeEvent);
        }
        if(name.equalsIgnoreCase(MouseOverEvent.NAME)){
            return new MouseOverEvent(nativeEvent);
        }
        if(name.equalsIgnoreCase(MouseUpEvent.NAME)){
            return new MouseUpEvent(nativeEvent);
        }

        //TODO:implement for other events
        return null;
    }
    public static String getEventHandlerType(EventHandler handler){
        if(handler instanceof BlurHandler){
            return BlurEvent.NAME;
        }
        if(handler instanceof ChangeHandler){
            return ChangeEvent.NAME;
        }
        if(handler instanceof ClickHandler){
            return ClickEvent.NAME;
        }
        if(handler instanceof ContextMenuHandler){
            return ContextMenuEvent.NAME;
        }
        if(handler instanceof DoubleClickHandler){
            return DoubleClickEvent.NAME;
        }
        if(handler instanceof FocusHandler){
            return FocusEvent.NAME;
        }
        if(handler instanceof KeyDownHandler){
            return KeyDownEvent.NAME;
        }
        if(handler instanceof KeyPressHandler){
            return KeyPressEvent.NAME;
        }
        if(handler instanceof KeyUpHandler){
            return KeyUpEvent.NAME;
        }
        if(handler instanceof LoadHandler){
            return LoadEvent.NAME;
        }
        if(handler instanceof MouseDownHandler){
            return MouseDownEvent.NAME;
        }
        if(handler instanceof MouseMoveHandler){
            return MouseMoveEvent.NAME;
        }
        if(handler instanceof MouseOutHandler){
            return MouseOutEvent.NAME;
        }
        if(handler instanceof MouseOverHandler){
            return MouseOverEvent.NAME;
        }
        if(handler instanceof MouseUpHandler){
            return MouseUpEvent.NAME;
        }
        return null;//throw exception over here
    }
     private static String getEventType(Event event){
        if(event instanceof BlurEvent){
            return BlurEvent.NAME;
        }
        if(event instanceof ChangeEvent){
            return ChangeEvent.NAME;
        }
        if(event instanceof ClickEvent){
            return ClickEvent.NAME;
        }
        if(event instanceof ContextMenuEvent){
            return ContextMenuEvent.NAME;
        }
        if(event instanceof DoubleClickEvent){
            return DoubleClickEvent.NAME;
        }
        if(event instanceof FocusEvent){
            return FocusEvent.NAME;
        }
        if(event instanceof KeyDownEvent){
            return KeyDownEvent.NAME;
        }
        if(event instanceof KeyPressEvent){
            return KeyPressEvent.NAME;
        }
        if(event instanceof KeyUpEvent){
            return KeyUpEvent.NAME;
        }
        if(event instanceof LoadEvent){
            return LoadEvent.NAME;
        }
        if(event instanceof MouseDownEvent){
            return MouseDownEvent.NAME;
        }
        if(event instanceof MouseMoveEvent){
            return MouseMoveEvent.NAME;
        }
        if(event instanceof MouseOutEvent){
            return MouseOutEvent.NAME;
        }
        if(event instanceof MouseOverEvent){
            return MouseOverEvent.NAME;
        }
        if(event instanceof MouseUpEvent){
            return MouseUpEvent.NAME;
        }
        return null;//throw exception over here
    }
}
